<?php
/**
 * Различные методы управления модулями в системе.
 *
 * @author nrnwest@gmail.com
 *
 */
class Arm_Module
{
    /**
     * Arm_Module
     *
     * @var Arm_Module
     */
    private static $_instance = null;
    
    /**
     * Конфиги модулей
     * @var array
     */
    private $_config = array();
    
    /**
     * данные про установленные модули в системе
     * @var array
     */
    private $_modulesData = null;
    
    /**
     * Arm_Module_Sql_Pdo_Mysql 
     * @var Arm_Module_Sql_Pdo_Mysql 
     */
    private $_sql;
    
    /**
     * Тип навигационых файлов.
     * @var string
     */
    private $_typeMenuFile = '.php';
    
    /**
     * Системные модули, данные модули нельзя отключить или удалить в системе.
     * @var array
     */
    private $_systemModule = array('user', 'admin', 'default');
    
    private function __construct()
    {
      $this->_sql = Arm_Db::getSqlObject('Arm_Module_Sql', Arm_Db::getAdapter());
      $this->getDataModules();
    }
    
    /**
     * Arm_Module
     *
     * @return Arm_Module
     */
    public static function getInstance()
    {
        if(self::$_instance === null) {
            self::$_instance = new Arm_Module();
        }
        return self::$_instance;
    }
    
    /**
     * Получения всех меню с директории navigation
     * @param string $module навазния модуля
     * @param bool $returnName если false вернет масив путей к файлам, иначе только 
     *                         масив названий модулей
     */
    public function getNavigations($module, $returnName = true)
    {
         $path  = Arm_Core::getPathModule($module) . 'install/navigation/';
         $menus = glob($path . "*$this->_typeMenuFile");  
         $reuslt = array();
         foreach ($menus as $v) {
             $reuslt[] = $returnName === true ? $this->_delTypeFileMenu(basename($v)) : $v;
         }
         return $reuslt;
    }
    
    protected function _delTypeFileMenu($name)
    {
      return str_replace($this->_typeMenuFile, '', $name);
    }
    
    /**
     * Вернет путь к файлу меню модуля, с учетом имени модуля, и названия меню
     * @param string $module названия модуля
     * @param string $nameMenu названия меню.
     */
    public function getPathMenu($module, $nameMenu)
    {
        return Arm_Core::getPathModule($module) . 'install/navigation/' . $nameMenu . $this->_typeMenuFile;
    }
    
    /**
     * Вернет тип файлов меню модулей.
     */
    public function getTypeMenuFile()
    {
      return $this->_typeMenuFile;
    }
    
    /**
     * Вернет массив системных модулей
     * 
     * @return array
     */
    public function getSystemModule()
    {
     return $this->_systemModule;
    }
    
    /**
     * Включить или выключить модуль
     * 
     * @param string $name
     * @param int    $mode 0 модуль выключим, 1 включим
     */
    public function onOff($name, $mode)
    {
       $mode = (int) $mode;
       if($mode !== 0 and $mode !== 1) {
           throw new Arm_Exception(Arm_Translate::_('Не допустимое значение второго параметра, допустимо 0 или 1'));
       }
       if($this->isModuleSystem($name)) {
           throw new Arm_Exception(Arm_Translate::_('Модуль %s являеться системным потому его отключить не возможно', $name));
       }
       $this->_sql->onOff($name, $mode);
       return;
    }
    
   /**
    * Проверит являеться ли модуль системным если да то вернет true иначе false
    * 
    * @param unknown_type $name
    */
   public function isModuleSystem($name)
   {
     $name = $this->filterModuleName($name);
     if(array_search($name, $this->_systemModule) === false) {
         return false;
     } else {
         return true;
     }
   }
   
   
   
   /**
    * Фильтрирует названия модуля.
    * @param string $name
    */
   public function filterModuleName($name)
   {
      return strtolower(trim($name));
   }
    
    /**
     * Вернет конфиг модуля.
     *
     *
     * @param $moduleName имя директории модуля
     * @return Zend_Config_Ini
     */
    public function getConfig($moduleName)
    {
        $moduleName = strtolower($moduleName);
        if(false !== array_key_exists($moduleName, $this->_config)) {
            return $this->_config[$moduleName];
        }
        $path = Arm_Core::getPathModule($moduleName)  . 'configs/application.ini';
        $this->_config[$moduleName] = new Zend_Config_Ini($path);
        return $this->_config[$moduleName];
    }
    
    /**
     * Получения настроек роутеров, для модуля.
     * 
     * @param string $module
     * @return Zend_Config|false
     */
    public function getConfigRoutes($module)
    {
        $path = Arm_Core::getPathModule($module) . 'configs/routes.ini';
        $cache  = Arm_Cache::getInstance()->getFrontendFile(array($path));
        $id = $module . 'routesini';
        if($cache->test($id)) {
            return $cache->load($id);
        }
        
        $config = new Zend_Config_Ini($path);
        if($config->routes === null) {
            $result = false;
        }
        $result  = $config->routes;
        $cache->save($result, $id);
        return $result;
    }
    
    /**
     * Получения настроект кеширования модуля
     * 
     * @param string $module
     * @return Zend_Config|false
     */
    public function getConfigCache($module)
    {
        $path = Arm_Core::getPathModule($module) . 'configs/cache.ini';
        $cache  = Arm_Cache::getInstance()->getFrontendFile(array($path));
        $id = $module . 'cacheini';
        if($cache->test($id)) {
            return $cache->load($id);
        }
        
        $config = new Zend_Config_Ini($path);
        if($config->page === null) {
            $result = false;
        }
        $result  = $config->page;
        $cache->save($result, $id);
        return $result;
    }
    
    /**
     * Вернет масив про информацию модулей, как установленных так и не установленых, но которые 
     * присутствую в директории application/
     * 
     * @return array
     */
    public function getDataModules()
    {
      $modulesFile = $this->getModulesIsFileSystem();  
     if($this->_modulesData === null) {
         try {
         $array = $this->_sql->getDb()->fetchAll($this->_sql->getModule());
         } catch (Exception $e) {
             $array = array();
         }
         $this->_modulesData = array();
         foreach ($array as $k=>$v) {
             $this->_modulesData[$v['name']] = $v;
         }
         // добавим не проистелированные модули
         foreach ($modulesFile as $v) {
             if(array_key_exists($v, $this->_modulesData) === true) {
                 continue;
             }
             $this->_modulesData[$v] = array('install'=>'0', 'name'=>$v);
         }
     }
     return $this->_modulesData;
    }
    
    /**
     * Сброс обьекта Arm_Module
     */
    public static function resetInstance()
    {
      self::$_instance = null;
      return;
    }
    
    /**
     * Вернет масив модулей которые присутсвуют в базе данных. В системной таблице arm_modules
     * как проистелированные так и нет.
     * 
     * @return array 
     */
    public function getModulesDb()
    {
       return $this->_sql->getDb()->fetchAll($this->_sql->getModule());
    }
    
    
    /**
     * Вернет масив модулей который существуют в файловой системе проекта. кроме модуля install
     * 
     * @return array
     */
    public function getModulesIsFileSystem()
    {
      $module = glob(Arm_Core::getPathModules() . '*', GLOB_ONLYDIR);
      $result = array();
      foreach ($module as $v) {
          if(is_dir($v . '\controllers')) {
          $name = basename($v);
          if($name === 'install') {
              continue;
          }
          $result[] = $name;
          }
      }
      return $result;
    }

    /**
     * Проверка существования модуля
     * @throw Arm_Exception Если не существует модуля
     * @param string $name имя модуля
     */
    protected function _isModule($name)
    {
      if(array_key_exists($name, $this->_modulesData) === false) {
          throw new Arm_Exception(Arm_Translate::_('Не существует модуля "%s" в системе', $name));
      }
    }
    
    /**
     * Вернет id модуля
     * @param string $name директория модуля
     */
    public function getIdModule($name)
    {
         $idModule = $this->_sql->getIdModule($name);
         if($idModule === false) {
             throw new Arm_Exception(Arm_Translate::_('В системе не существует модуля: %s', $name));
         }
         return (int) $idModule;
    }
    
    /**
     * Получения имени модуля по его id
     * 
     * @param int $idModule
     * @return string
     */
    public function getNameModule($idModule)
    {
       return $this->_sql->getModuleName($idModule);
    }
    
    /**
     * Проверяет существует директория модуля в application/modules/
     * @param  string $name
     * @return bool
     */
    public function isModuleApplication($name)
    {
       return  is_dir(Arm_Core::getPathModule($name));
    }
    
    /**
     * Запись инстеляции модуля, если $module null то создаются инстеляции
     * для всех модулей которые числятся в базе данных.
     * 
     * 1) Запись меню модуля
     * 2) Запись страниц модуля
     * 4) @TODO Запись дампа таблиц модуля module_ 
     * 
     * @param string|null $module
     */
    public function createModuleIstall($module = null)
    {
      if($module === null) {
          $modules = Arm_Module::getInstance()->getModulesDb();
      } else {
          $modules = array();  
          $modules[]['name'] = $module; 
      }
      $navigation = new Arm_Navigation();
      $page = new Arm_Page();
      $cron = new Arm_Cron();
      $dump = new Arm_Db_Dump(Arm_Db::getAdapter());
      foreach ($modules as $v) {
          $dump->exportTablesModule($v['name']);
          $navigation->writeMenusModule($v['name']);
          $idModule = $this->getIdModule($v['name']);
          $page->createPageInstallModule($idModule);
          $cron->writeDampComandsModule($idModule, $v['name']);
      }
      return;
    }
    
    /**
     * Вернет архивы модулей загруженных в систему.
     * 
     * @return array
     */
    public function getArchivsInstall()
    {
        $path = Arm_Core::getPathModule('install') . 'install/modules/';
        $archivs = glob($path . '*' . Arm_Module_Install::TYPE_ARCHIV_MODULE);
        if($archivs === false) {
            return array();    
        }
        return $archivs;
    }
    
}